﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;

namespace SimpleReactiveObjects.Test
{
    [TestClass]
    public class WeakObservableTest
    {
        private class FooException : Exception
        {
        }

        public class TestState
        {
            public int? LatestValue
            {
                get; set;
            }

            public bool IsCompleted
            {
                get; set;
            }

            public bool IsError
            {
                get; set;
            }
        }

        private class TestObserver : IObserver<int>
        {
            TestState state;

            public TestObserver(TestState state)
            {
                this.state = state;
            }

            public void OnCompleted()
            {
                state.IsCompleted = true;

                var s = "OnCompleted()";
                Debug.WriteLine(s);
            }

            public void OnError(Exception error)
            {
                state.IsError = true;

                var s = string.Format("OnError({0})", error.GetType().Name);
                Debug.WriteLine(s);
            }

            public void OnNext(int value)
            {
                state.LatestValue = value;

                var s = string.Format("OnNext({0})", value);
                Debug.WriteLine(s);
            }
        }


        [TestMethod]
        public void TestOnNext()
        {
            int? n = null;
            
            var source = new Subject<int>();
            var d = source.AsWeakObservable().Subscribe(x => n = x);

            GC.Collect();

            source.OnNext(1);
            n.Is(1);

            GC.KeepAlive(d);
        }

        [TestMethod]
        public void TestOnNext2()
        {
            int? n = null;

            var source = new Subject<int>();
            var d = source.AsWeakObservable().Subscribe(x => n = x);
            d = null;

            GC.Collect();

            source.OnNext(1);
            n.IsNull();
        }

        [TestMethod]
        public void TestOnCompleted()
        {
            var source = new Subject<int>();
            var s = new TestState();
            var o = new TestObserver(s);
            var d = source.AsWeakObservable().Subscribe(o);

            s.LatestValue.IsNull();

            GC.Collect();

            source.OnNext(1);
            s.LatestValue.Is(1);

            GC.Collect();

            source.OnCompleted();
            source.OnNext(2);
            s.LatestValue.Is(1);
            s.IsCompleted.IsTrue();
            s.IsError.IsFalse();

            GC.KeepAlive(d);
            GC.KeepAlive(o);
            GC.KeepAlive(s);
            GC.KeepAlive(source);
        }

        [TestMethod]
        public void TestOnCompleted2()
        {
            var source = new Subject<int>();
            var s = new TestState();
            var o = new TestObserver(s);
            var d = source.AsWeakObservable().Subscribe(o);
            o = null;

            s.LatestValue.IsNull();

            GC.Collect();

            source.OnNext(1);
            s.LatestValue.Is(1);

            GC.KeepAlive(d);
            d = null;
            GC.Collect();

            source.OnCompleted();
            source.OnNext(2);

            s.LatestValue.Is(1);
            s.IsCompleted.IsFalse();
            s.IsError.IsFalse();

            GC.KeepAlive(source);
            GC.KeepAlive(s);
        }

        [TestMethod]
        public void TestOnError()
        {
            var source = new Subject<int>();
            var s = new TestState();
            var o = new TestObserver(s);
            var d = source.AsWeakObservable().Subscribe(o);

            s.LatestValue.IsNull();

            GC.Collect();

            source.OnNext(1);
            s.LatestValue.Is(1);

            GC.Collect();

            source.OnError(new FooException());
            source.OnNext(2);
            s.LatestValue.Is(1);
            s.IsCompleted.IsFalse();
            s.IsError.IsTrue();

            GC.KeepAlive(d);
            GC.KeepAlive(o);
            GC.KeepAlive(s);
            GC.KeepAlive(source);
        }

        [TestMethod]
        public void TestOnError2()
        {
            var source = new Subject<int>();
            var s = new TestState();
            var o = new TestObserver(s);
            var d = source.AsWeakObservable().Subscribe(o);
            o = null;

            s.LatestValue.IsNull();

            GC.Collect();

            source.OnNext(1);
            s.LatestValue.Is(1);

            GC.KeepAlive(d);
            d = null;
            GC.Collect();

            source.OnError(new FooException());
            source.OnNext(2);

            s.LatestValue.Is(1);
            s.IsCompleted.IsFalse();
            s.IsError.IsFalse();

            GC.KeepAlive(source);
            GC.KeepAlive(s);
        }
    }
}
